package com.essence.grabiec104.grab.executor;

import com.essence.common.model.BytesFrame;
import com.essence.common.model.Substation;
import com.essence.grabiec104.grab.service.AlarmService;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import org.pcap4j.core.PacketListener;
import org.pcap4j.core.PcapPacket;
import org.pcap4j.packet.IpV4Packet;
import org.pcap4j.packet.TcpPacket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

/**
 * @brief iec104数据包解析器
 * @details 解析iec104数据包，并分发给异步处理线程
 */
public class PacketListenerCodec implements PacketListener {
    private static final Logger LOG = LoggerFactory.getLogger(PacketListenerCodec.class);
    private AsyncHandlerDispatcher asyncHandlerDispatcher;
    private ByteBuf byteBuf;
    private AlarmService alarmService;
    private String dataPlane;


    public PacketListenerCodec(int initialCapacity,
                               AsyncHandlerDispatcher asyncHandlerDispatcher,
                               AlarmService alarmService,
                               String dataPlane) {
        this.byteBuf = PooledByteBufAllocator.DEFAULT.buffer(initialCapacity * 100);
        this.asyncHandlerDispatcher = asyncHandlerDispatcher;
        this.alarmService = alarmService;
        this.dataPlane = dataPlane;
    }

    @Override
    public void gotPacket(PcapPacket packet) {
        if (packet.contains(IpV4Packet.class)) {
            IpV4Packet pkg = packet.get(IpV4Packet.class);
            if (pkg.getPayload() != null
                    && pkg.getPayload().getRawData() != null
                    && pkg.getPayload().getRawData().length > 0) {
                String dstHost = pkg.getHeader().getDstAddr().getHostAddress();
                String srcHost = pkg.getHeader().getSrcAddr().getHostAddress();
                int srcPort = 0;
                int dstPort = 0;
                if (packet.contains(TcpPacket.class)) {
                    TcpPacket tcpPacket = packet.get(TcpPacket.class);
                    srcPort = tcpPacket.getHeader().getSrcPort().valueAsInt();
                    dstPort = tcpPacket.getHeader().getDstPort().valueAsInt();
                }
                List<Substation> stationGroup = alarmService.getSubstationGroup();

                BytesFrame.DirectionEnum direction = null;
                for (Substation mainStation : stationGroup) {
                    if (mainStation.getHost().equals(srcHost) || mainStation.getHost().equals(dstHost)) {
                        for (Substation slave : mainStation.getSlaveStation()) {
                            if (slave.getHost().equals(srcHost) && mainStation.getHost().equals(dstHost)) {
                                if(slave.getPort() == srcPort) {
                                    direction = BytesFrame.DirectionEnum.Upstream;
                                }
                            }
                            if (slave.getHost().equals(dstHost) && mainStation.getHost().equals(srcHost)) {
                                if(slave.getPort() == dstPort) {
                                    direction = BytesFrame.DirectionEnum.Downstream;
                                }
                            }
                        }
                    }
                }

                if (direction == null) {
                    return;
                }

                byte[] payload = pkg.getPayload().getRawData();
                byteBuf.writeBytes(payload);
                while (byteBuf.readableBytes() > 2) {
                    // 标记读取位置，读取一个字节，如果不是flag，则继续读取下一个字节，直到找到flag
                    byteBuf.markReaderIndex();
                    byte flag = byteBuf.readByte();
                    if (flag != BytesFrame.FLAG) {
                        continue;
                    }
                    // 读取apdu长度，如果长度小于0或者大于最大长度，则从头开始读取
                    int apduLength = byteBuf.readUnsignedByte();
                    if (apduLength < 4 || apduLength > BytesFrame.MAX_LENGTH) {
                        continue;
                    }
                    // 如果剩余长度小于apdu长度，则重置读取位置并跳出循环等待下一个包
                    if (byteBuf.readableBytes() < apduLength) {
                        byteBuf.resetReaderIndex();
                        break;
                    }
                    byte[] apduBytes = new byte[apduLength];
                    byteBuf.readBytes(apduBytes);
                    try {
                        BytesFrame frame = new BytesFrame(apduBytes, srcHost, dstHost, srcPort, dstPort, direction, dataPlane);
                        // 异步分发
                        asyncHandlerDispatcher.dispatch(frame);
                    } catch (Exception ex) {
                        LOG.error("解析数据包失败", ex);

                    }

                }

                if (byteBuf.readerIndex() > byteBuf.capacity() * 0.7) {
                    LOG.info("清理缓冲区...");
                    byteBuf.discardReadBytes();
                }
            }
        }
    }

}
